﻿#region 참조 네임스페이스

using System;
using System.ComponentModel;
using System.Windows.Forms;
using Skying.ComponentModel;
using Skying.Net.Sockets;
using Skying.Threading;
using System.Windows.Forms.DataVisualization.Charting;
using System.Diagnostics;
using Skying.Diagnostics.Logger;

#endregion

namespace TcpServer
{
	public partial class MainForm : Form
	{
		#region 변수

		private Logger _logger = Logger.GetLogger();

		/// <summary>
		/// 클라이언트의 접속을 받아들이는 TCP 리스너를 나타낸다.
		/// </summary>
		private TcpListener _listener;

		/// <summary>
		/// 로깅을 위한 데이터 그리드 바인딩 리스트를 나타낸다.
		/// </summary>
		private SortableBindingList<Log> _logBindingList;

		/// <summary>
		/// 프로세스의 CPU 사용량 측정을 위한 타이머를 나타낸다.
		/// </summary>
		private SynchronizingTimer _cpuUsagePerformanceTimer;

		#endregion

		#region 생성자/소멸자

		public MainForm()
		{
			this.InitializeComponent();

			this._logBindingList = new SortableBindingList<Log>();

			this.LogDataGridView.DataSource = this._logBindingList;
		}

		#endregion

		#region 로깅

		/// <summary>
		/// 로그 그리드 뷰에 로그를 남긴다.
		/// </summary>
		/// <param name="logString">로그 문자열을 설정한다.</param>
		private void Log(string logString)
		{
			try
			{
				string nowString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss,fff");

				this._logBindingList.Add(new Log(nowString, logString));

				this.LogDataGridView.Sort(this.LogDataGridView.Columns[0], ListSortDirection.Descending);
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		#endregion

		#region 차트

		/// <summary>
		/// CPU 사용량 측정을 시작한다.
		/// </summary>
		private void StartCpuUsageMearsurement()
		{
			this.ProcessCpuPerformanceCounter.CategoryName = "Process";
			this.ProcessCpuPerformanceCounter.CounterName = "% Processor Time";
			this.ProcessCpuPerformanceCounter.InstanceName = Process.GetCurrentProcess().ProcessName;

			this._cpuUsagePerformanceTimer = new SynchronizingTimer();

			this._cpuUsagePerformanceTimer.Elapsed += () =>
			{
				DateTime now = DateTime.Now;

				if (this.ProcessCpuChart.Series[0].Points.Count > 20)
				{
					this.ProcessCpuChart.Series[0].Points.RemoveAt(0);
				}

				this.ProcessCpuChart.Series[0].Points.AddXY(now, this.ProcessCpuPerformanceCounter.NextValue());

				this.ProcessCpuChart.ResetAutoValues();
			};

			this._cpuUsagePerformanceTimer.SetPeriodic(new TimeSpan(0, 0, 1));
		}

		/// <summary>
		/// CPU 사용량 측정을 중지한다.
		/// </summary>
		private void StopCpuUsageMeasurement()
		{
			this._cpuUsagePerformanceTimer.Cancel();
		}

		#endregion

		#region 버튼 이벤트

		/// <summary>
		/// Start 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void StartButton_Click(object sender, EventArgs e)
		{
			try
			{
				this.ClientsListView.Items.Clear();

				int portNumber = (int)this.PortNumericUpDown.Value;

				this._listener = new TcpListener(portNumber);

				this._listener.ListeningStarted = (eventArgs) =>
				{
					string logString = string.Format("Listening @ {0}:{1}", this._listener.Address, this._listener.Port);

					this.ListenerStripStatusLabel.Text = logString;

					this.StartCpuUsageMearsurement();

					this.StartButton.Enabled = false;
					this.StopButton.Enabled = true;
					this.BroadcastButton.Enabled = true;
					this.SendToButton.Enabled = true;
					this.SendObjectToButton.Enabled = true;

					this.Log(logString);
				};

				this._listener.ListeningStopped = (eventArgs) =>
				{
					this.ListenerStripStatusLabel.Text = "Stopped";

					this.StopCpuUsageMeasurement();

					this.StartButton.Enabled = true;
					this.StopButton.Enabled = false;
					this.BroadcastButton.Enabled = false;
					this.SendToButton.Enabled = false;
					this.SendObjectToButton.Enabled = false;

					this.Log("Stopped");
				};

				this._listener.AcceptCompleted = (eventArgs) =>
				{
					this.ClientsListView.Items.Add(eventArgs.Result.Id.ToString(), eventArgs.Result.Id.ToString(), -1);

					this.ConnectedClientCountStripStatusLabel.Text = string.Format("{0} connected", this._listener.ClientsCount);

					this.Log(string.Format("Client {0} conncedted from {1}:{2}(UserToken : {3})", eventArgs.Result.Id, eventArgs.Result.Address, eventArgs.Result.Port, eventArgs.Result.UserToken));
				};

				this._listener.ClientDisconnected = (eventArgs) =>
				{
					this.ClientsListView.Items.RemoveByKey(eventArgs.Result.Id.ToString());

					this.ConnectedClientCountStripStatusLabel.Text = string.Format("{0} connected", this._listener.ClientsCount);

					this.Log(string.Format("Client {0} disconnected from {1}:{2}", eventArgs.Result.Id, eventArgs.Result.Address, eventArgs.Result.Port));
				};

				this._listener.Received = (eventArgs) =>
				{
					TransferEventArgs result = eventArgs.Result;

					string logString = string.Empty;

					Header header = result.Header;

					switch (header.PacketType)
					{
						case PacketType.Raw:

							logString = string.Format("[R] Raw (length : {0})", eventArgs.Result.DataStream.Length);

							break;

						case PacketType.Serialized:

							if (header.DataType.Equals(typeof(DateTime).FullName))
							{
								DateTime dataObject = (DateTime)result.DataObject;

								logString = string.Format("[R] Serialized Object (Type: {0}, Value: {1})", header.DataType, dataObject);
							}

							break;

						case PacketType.Message:

							logString = string.Format("[R] Message (Value: {0})", result.DataMessage);

							break;

						case PacketType.File:

							break;
					}

					this.Log(logString);
				};

				this._listener.PartReceived = (eventArgs) =>
				{
					DateTime now = DateTime.Now;

					this.ReceivedDataCountChart.Series[0].Points.AddXY(now, eventArgs.Result);
				};

				this._listener.Error = (eventArgs) =>
				{
					this.Log(eventArgs.Result.Message);
				};

				this._listener.Listen();
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		/// <summary>
		/// Stop 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void StopButton_Click(object sender, EventArgs e)
		{
			try
			{
				if (!this._listener.IsListening) return;

				this.ClientsListView.Items.Clear();

				this._listener.Close();
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		/// <summary>
		/// Broadcast 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void BroadcastButton_Click(object sender, EventArgs e)
		{
			try
			{
				this._listener.Broadcast(this.MessageTextBox.Text);
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		/// <summary>
		/// SendTo 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void SendToButton_Click(object sender, EventArgs e)
		{
			try
			{
				if (this.ClientsListView.SelectedItems.Count > 0)
				{
					Console.WriteLine(this.ClientsListView.SelectedItems[0].Text);

					this._listener.SendTo(this.ClientsListView.SelectedItems[0].Text, this.MessageTextBox.Text);
				}
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		/// <summary>
		/// SendObjectTo 버튼이 클릭됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void SendObjectToButton_Click(object sender, EventArgs e)
		{
			try
			{
				if (this.ClientsListView.SelectedItems.Count > 0)
				{
					Console.WriteLine(this.ClientsListView.SelectedItems[0].Text);

					this._listener.SendTo(this.ClientsListView.SelectedItems[0].Text, DateTime.Now);
				}
			}
			catch (Exception error)
			{
				this._logger.Error(error);
			}
		}

		#endregion

		#region 패널 이벤트

		/// <summary>
		/// 메시지 텍스트 박스의 패널이 리사이즈됐을 때 발생하는 이벤트를 구현한다.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void panel3_Resize(object sender, EventArgs e)
		{
			Panel panel = sender as Panel;

			if (panel.Controls.Count > 0)
			{
				panel.Controls[0].Width = panel.Width - panel.Controls[0].Left - panel.Padding.Left - panel.Padding.Right;
			}
		}

		#endregion
	}
}